package org.apache.maven.project;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.artifact.InvalidRepositoryException;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.model.Build;
import org.apache.maven.model.CiManagement;
import org.apache.maven.model.Contributor;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Developer;
import org.apache.maven.model.DistributionManagement;
import org.apache.maven.model.Extension;
import org.apache.maven.model.IssueManagement;
import org.apache.maven.model.License;
import org.apache.maven.model.MailingList;
import org.apache.maven.model.Model;
import org.apache.maven.model.Organization;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.model.Prerequisites;
import org.apache.maven.model.Profile;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.ReportSet;
import org.apache.maven.model.Reporting;
import org.apache.maven.model.Repository;
import org.apache.maven.model.Resource;
import org.apache.maven.model.Scm;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.project.artifact.InvalidDependencyVersionException;
import org.apache.maven.project.artifact.MavenMetadataSource;
import org.apache.maven.repository.RepositorySystem;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.repository.RemoteRepository;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * The concern of the project is provide runtime values based on the model.
 * <p/>
 * The values in the model remain untouched but during the process of building a project notions
 * like inheritance and interpolation can be added. This allows to have an entity which is useful in
 * a runtime while preserving the model so that it can be marshalled and unmarshalled without being
 * tainted by runtime requirements.
 * <p/>
 * We need to leave the model intact because we don't want the following:
 * <ol>
 * <li>We don't want interpolated values being written back into the model.
 * <li>We don't want inherited values being written back into the model.
 * </ol>
 */
public class MavenProject implements Cloneable {
  public static final String EMPTY_PROJECT_GROUP_ID = "unknown";

  public static final String EMPTY_PROJECT_ARTIFACT_ID = "empty-project";

  public static final String EMPTY_PROJECT_VERSION = "0";

  private Model model;

  private MavenProject parent;

  private File file;

  private Set<Artifact> resolvedArtifacts;

  private ArtifactFilter artifactFilter;

  private Set<Artifact> artifacts;

  private Artifact parentArtifact;

  private Set<Artifact> pluginArtifacts;

  private List<ArtifactRepository> remoteArtifactRepositories;

  private List<ArtifactRepository> pluginArtifactRepositories;

  private List<RemoteRepository> remoteProjectRepositories;

  private List<RemoteRepository> remotePluginRepositories;

  private List<Artifact> attachedArtifacts;

  private MavenProject executionProject;

  private List<MavenProject> collectedProjects;

  private List<String> compileSourceRoots = new ArrayList<String>();

  private List<String> testCompileSourceRoots = new ArrayList<String>();

  private List<String> scriptSourceRoots = new ArrayList<String>();

  private ArtifactRepository releaseArtifactRepository;

  private ArtifactRepository snapshotArtifactRepository;

  private List<Profile> activeProfiles = new ArrayList<Profile>();

  private Map<String, List<String>> injectedProfileIds = new LinkedHashMap<String, List<String>>();

  private Set<Artifact> dependencyArtifacts;

  private Artifact artifact;

  // calculated.
  private Map<String, Artifact> artifactMap;

  private Model originalModel;

  private Map<String, Artifact> pluginArtifactMap;

  private Set<Artifact> reportArtifacts;

  private Map<String, Artifact> reportArtifactMap;

  private Set<Artifact> extensionArtifacts;

  private Map<String, Artifact> extensionArtifactMap;

  private Map<String, Artifact> managedVersionMap;

  private Map<String, MavenProject> projectReferences = new HashMap<String, MavenProject>();

  private boolean executionRoot;

  private Map<String, String> moduleAdjustments;

  private ProjectBuilder mavenProjectBuilder;

  private ProjectBuildingRequest projectBuilderConfiguration;

  private RepositorySystem repositorySystem;

  private File parentFile;

  private Map<String, Object> context;

  private ClassRealm classRealm;

  private DependencyFilter extensionDependencyFilter;

  private final Set<String> lifecyclePhases = Collections.synchronizedSet(new LinkedHashSet<String>());

  private Logger logger;

  public MavenProject() {
    Model model = new Model();

    model.setGroupId(EMPTY_PROJECT_GROUP_ID);
    model.setArtifactId(EMPTY_PROJECT_ARTIFACT_ID);
    model.setVersion(EMPTY_PROJECT_VERSION);

    setModel(model);
  }

  public MavenProject(Model model) {
    setModel(model);
  }

  /**
   * @deprecated use {@link #clone()} so subclasses can provide a copy of the same class
   */
  @Deprecated
  public MavenProject(MavenProject project) {
    repositorySystem = project.repositorySystem;
    logger = project.logger;
    mavenProjectBuilder = project.mavenProjectBuilder;
    projectBuilderConfiguration = project.projectBuilderConfiguration;
    deepCopy(project);
  }

  @Deprecated
  public MavenProject(Model model, RepositorySystem repositorySystem) {
    this.repositorySystem = repositorySystem;
    setModel(model);
  }

  public File getParentFile() {
    return parentFile;
  }

  public void setParentFile(File parentFile) {
    this.parentFile = parentFile;
  }

  /**
   * Constructor
   * 
   * @param repositorySystem - may not be null
   * @param mavenProjectBuilder
   * @param projectBuilderConfiguration
   * @throws InvalidRepositoryException
   */
  MavenProject(RepositorySystem repositorySystem, ProjectBuilder mavenProjectBuilder, ProjectBuildingRequest projectBuilderConfiguration, Logger logger) {
    if (repositorySystem == null) {
      throw new IllegalArgumentException("mavenTools: null");
    }

    this.mavenProjectBuilder = mavenProjectBuilder;
    this.projectBuilderConfiguration = projectBuilderConfiguration;
    this.repositorySystem = repositorySystem;
    this.logger = logger;
  }

  @Deprecated
  public Set<Artifact> createArtifacts(ArtifactFactory artifactFactory, String inheritedScope, ArtifactFilter filter)
      throws InvalidDependencyVersionException {
    return MavenMetadataSource.createArtifacts(artifactFactory, getDependencies(), inheritedScope, filter, this);
  }

  // TODO: Find a way to use <relativePath/> here...it's tricky, because the moduleProject
  // usually doesn't have a file associated with it yet.
  public String getModulePathAdjustment(MavenProject moduleProject) throws IOException {
    // FIXME: This is hacky. What if module directory doesn't match artifactid, and parent
    // is coming from the repository??
    String module = moduleProject.getArtifactId();

    File moduleFile = moduleProject.getFile();

    if (moduleFile != null) {
      File moduleDir = moduleFile.getCanonicalFile().getParentFile();

      module = moduleDir.getName();
    }

    if (moduleAdjustments == null) {
      moduleAdjustments = new HashMap<String, String>();

      List<String> modules = getModules();
      if (modules != null) {
        for (String modulePath : modules) {
          String moduleName = modulePath;

          if (moduleName.endsWith("/") || moduleName.endsWith("\\")) {
            moduleName = moduleName.substring(0, moduleName.length() - 1);
          }

          int lastSlash = moduleName.lastIndexOf('/');

          if (lastSlash < 0) {
            lastSlash = moduleName.lastIndexOf('\\');
          }

          String adjustment = null;

          if (lastSlash > -1) {
            moduleName = moduleName.substring(lastSlash + 1);
            adjustment = modulePath.substring(0, lastSlash);
          }

          moduleAdjustments.put(moduleName, adjustment);
        }
      }
    }

    return moduleAdjustments.get(module);
  }

  // ----------------------------------------------------------------------
  // Accessors
  // ----------------------------------------------------------------------

  public Artifact getArtifact() {
    return artifact;
  }

  public void setArtifact(Artifact artifact) {
    this.artifact = artifact;
  }

  // @todo I would like to get rid of this. jvz.
  public Model getModel() {
    return model;
  }

  public MavenProject getParent() {
    if (parent == null) {
      /*
       * TODO: This is suboptimal. Without a cache in the project builder, rebuilding the parent
       * chain currently causes O(n^2) parser invocations for an inheritance hierarchy of depth n.
       */
      if (parentFile != null) {
        checkProjectBuildingRequest();
        ProjectBuildingRequest request = new DefaultProjectBuildingRequest(projectBuilderConfiguration);
        request.setRemoteRepositories(getRemoteArtifactRepositories());

        try {
          parent = mavenProjectBuilder.build(parentFile, request).getProject();
        } catch (ProjectBuildingException e) {
          throw new IllegalStateException("Failed to build parent project for " + getId(), e);
        }
      } else if (model.getParent() != null) {
        checkProjectBuildingRequest();
        ProjectBuildingRequest request = new DefaultProjectBuildingRequest(projectBuilderConfiguration);
        request.setRemoteRepositories(getRemoteArtifactRepositories());

        try {
          parent = mavenProjectBuilder.build(getParentArtifact(), request).getProject();
        } catch (ProjectBuildingException e) {
          throw new IllegalStateException("Failed to build parent project for " + getId(), e);
        }
      }
    }
    return parent;
  }

  public void setParent(MavenProject parent) {
    this.parent = parent;
  }

  public boolean hasParent() {
    return getParent() != null;
  }

  public File getFile() {
    return file;
  }

  public void setFile(File file) {
    this.file = file;
  }

  public File getBasedir() {
    if (getFile() != null) {
      return getFile().getParentFile();
    } else {
      // repository based POM
      return null;
    }
  }

  public void setDependencies(List<Dependency> dependencies) {
    getModel().setDependencies(dependencies);
  }

  public List<Dependency> getDependencies() {
    return getModel().getDependencies();
  }

  public DependencyManagement getDependencyManagement() {
    return getModel().getDependencyManagement();
  }

  // ----------------------------------------------------------------------
  // Test and compile sourceroots.
  // ----------------------------------------------------------------------

  private void addPath(List<String> paths, String path) {
    if (path != null) {
      path = path.trim();
      if (path.length() > 0) {
        File file = new File(path);
        if (file.isAbsolute()) {
          path = file.getAbsolutePath();
        } else {
          path = new File(getBasedir(), path).getAbsolutePath();
        }

        if (!paths.contains(path)) {
          paths.add(path);
        }
      }
    }
  }

  public void addCompileSourceRoot(String path) {
    addPath(getCompileSourceRoots(), path);
  }

  public void addScriptSourceRoot(String path) {
    if (path != null) {
      path = path.trim();
      if (path.length() != 0) {
        if (!getScriptSourceRoots().contains(path)) {
          getScriptSourceRoots().add(path);
        }
      }
    }
  }

  public void addTestCompileSourceRoot(String path) {
    addPath(getTestCompileSourceRoots(), path);
  }

  public List<String> getCompileSourceRoots() {
    return compileSourceRoots;
  }

  public List<String> getScriptSourceRoots() {
    return scriptSourceRoots;
  }

  public List<String> getTestCompileSourceRoots() {
    return testCompileSourceRoots;
  }

  public List<String> getCompileClasspathElements() throws DependencyResolutionRequiredException {
    List<String> list = new ArrayList<String>(getArtifacts().size() + 1);

    String d = getBuild().getOutputDirectory();
    if (d != null) {
      list.add(d);
    }

    for (Artifact a : getArtifacts()) {
      if (a.getArtifactHandler().isAddedToClasspath()) {
        // TODO: let the scope handler deal with this
        if (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_PROVIDED.equals(a.getScope())
            || Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
          addArtifactPath(a, list);
        }
      }
    }

    return list;
  }

  @Deprecated
  public List<Artifact> getCompileArtifacts() {
    List<Artifact> list = new ArrayList<Artifact>(getArtifacts().size());

    for (Artifact a : getArtifacts()) {
      // TODO: classpath check doesn't belong here - that's the other method
      if (a.getArtifactHandler().isAddedToClasspath()) {
        // TODO: let the scope handler deal with this
        if (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_PROVIDED.equals(a.getScope())
            || Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
          list.add(a);
        }
      }
    }
    return list;
  }

  @Deprecated
  public List<Dependency> getCompileDependencies() {
    Set<Artifact> artifacts = getArtifacts();

    if ((artifacts == null) || artifacts.isEmpty()) {
      return Collections.emptyList();
    }

    List<Dependency> list = new ArrayList<Dependency>(artifacts.size());

    for (Artifact a : getArtifacts()) {
      // TODO: let the scope handler deal with this
      if (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_PROVIDED.equals(a.getScope())
          || Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
        Dependency dependency = new Dependency();

        dependency.setArtifactId(a.getArtifactId());
        dependency.setGroupId(a.getGroupId());
        dependency.setVersion(a.getVersion());
        dependency.setScope(a.getScope());
        dependency.setType(a.getType());
        dependency.setClassifier(a.getClassifier());

        list.add(dependency);
      }
    }
    return list;
  }

  // TODO: this checking for file == null happens because the resolver has been confused about the
  // root
  // artifact or not. things like the stupid dummy artifact coming from surefire.
  public List<String> getTestClasspathElements() throws DependencyResolutionRequiredException {
    List<String> list = new ArrayList<String>(getArtifacts().size() + 2);

    String d = getBuild().getTestOutputDirectory();
    if (d != null) {
      list.add(d);
    }

    d = getBuild().getOutputDirectory();
    if (d != null) {
      list.add(d);
    }

    for (Artifact a : getArtifacts()) {
      if (a.getArtifactHandler().isAddedToClasspath()) {
        addArtifactPath(a, list);
      }
    }

    return list;
  }

  @Deprecated
  public List<Artifact> getTestArtifacts() {
    List<Artifact> list = new ArrayList<Artifact>(getArtifacts().size());

    for (Artifact a : getArtifacts()) {
      // TODO: classpath check doesn't belong here - that's the other method
      if (a.getArtifactHandler().isAddedToClasspath()) {
        list.add(a);
      }
    }
    return list;
  }

  @Deprecated
  public List<Dependency> getTestDependencies() {
    Set<Artifact> artifacts = getArtifacts();

    if ((artifacts == null) || artifacts.isEmpty()) {
      return Collections.emptyList();
    }

    List<Dependency> list = new ArrayList<Dependency>(artifacts.size());

    for (Artifact a : getArtifacts()) {
      Dependency dependency = new Dependency();

      dependency.setArtifactId(a.getArtifactId());
      dependency.setGroupId(a.getGroupId());
      dependency.setVersion(a.getVersion());
      dependency.setScope(a.getScope());
      dependency.setType(a.getType());
      dependency.setClassifier(a.getClassifier());

      list.add(dependency);
    }
    return list;
  }

  public List<String> getRuntimeClasspathElements() throws DependencyResolutionRequiredException {
    List<String> list = new ArrayList<String>(getArtifacts().size() + 1);

    String d = getBuild().getOutputDirectory();
    if (d != null) {
      list.add(d);
    }

    for (Artifact a : getArtifacts()) {
      if (a.getArtifactHandler().isAddedToClasspath()) {
        // TODO: let the scope handler deal with this
        if (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_RUNTIME.equals(a.getScope())) {
          addArtifactPath(a, list);
        }
      }
    }
    return list;
  }

  @Deprecated
  public List<Artifact> getRuntimeArtifacts() {
    List<Artifact> list = new ArrayList<Artifact>(getArtifacts().size());

    for (Artifact a : getArtifacts()) {
      // TODO: classpath check doesn't belong here - that's the other method
      if (a.getArtifactHandler().isAddedToClasspath()) {
        // TODO: let the scope handler deal with this
        if (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_RUNTIME.equals(a.getScope())) {
          list.add(a);
        }
      }
    }
    return list;
  }

  @Deprecated
  public List<Dependency> getRuntimeDependencies() {
    Set<Artifact> artifacts = getArtifacts();

    if ((artifacts == null) || artifacts.isEmpty()) {
      return Collections.emptyList();
    }

    List<Dependency> list = new ArrayList<Dependency>(artifacts.size());

    for (Artifact a : getArtifacts()) {
      // TODO: let the scope handler deal with this
      if (Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_RUNTIME.equals(a.getScope())) {
        Dependency dependency = new Dependency();

        dependency.setArtifactId(a.getArtifactId());
        dependency.setGroupId(a.getGroupId());
        dependency.setVersion(a.getVersion());
        dependency.setScope(a.getScope());
        dependency.setType(a.getType());
        dependency.setClassifier(a.getClassifier());

        list.add(dependency);
      }
    }
    return list;
  }

  public List<String> getSystemClasspathElements() throws DependencyResolutionRequiredException {
    List<String> list = new ArrayList<String>(getArtifacts().size());

    String d = getBuild().getOutputDirectory();
    if (d != null) {
      list.add(d);
    }

    for (Artifact a : getArtifacts()) {
      if (a.getArtifactHandler().isAddedToClasspath()) {
        // TODO: let the scope handler deal with this
        if (Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
          addArtifactPath(a, list);
        }
      }
    }
    return list;
  }

  @Deprecated
  public List<Artifact> getSystemArtifacts() {
    List<Artifact> list = new ArrayList<Artifact>(getArtifacts().size());

    for (Artifact a : getArtifacts()) {
      // TODO: classpath check doesn't belong here - that's the other method
      if (a.getArtifactHandler().isAddedToClasspath()) {
        // TODO: let the scope handler deal with this
        if (Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
          list.add(a);
        }
      }
    }
    return list;
  }

  @Deprecated
  public List<Dependency> getSystemDependencies() {
    Set<Artifact> artifacts = getArtifacts();

    if ((artifacts == null) || artifacts.isEmpty()) {
      return Collections.emptyList();
    }

    List<Dependency> list = new ArrayList<Dependency>(artifacts.size());

    for (Artifact a : getArtifacts()) {
      // TODO: let the scope handler deal with this
      if (Artifact.SCOPE_SYSTEM.equals(a.getScope())) {
        Dependency dependency = new Dependency();

        dependency.setArtifactId(a.getArtifactId());
        dependency.setGroupId(a.getGroupId());
        dependency.setVersion(a.getVersion());
        dependency.setScope(a.getScope());
        dependency.setType(a.getType());
        dependency.setClassifier(a.getClassifier());

        list.add(dependency);
      }
    }
    return list;
  }

  // ----------------------------------------------------------------------
  // Delegate to the model
  // ----------------------------------------------------------------------

  public void setModelVersion(String pomVersion) {
    getModel().setModelVersion(pomVersion);
  }

  public String getModelVersion() {
    return getModel().getModelVersion();
  }

  public String getId() {
    return getModel().getId();
  }

  public void setGroupId(String groupId) {
    getModel().setGroupId(groupId);
  }

  public String getGroupId() {
    String groupId = getModel().getGroupId();

    if ((groupId == null) && (getModel().getParent() != null)) {
      groupId = getModel().getParent().getGroupId();
    }

    return groupId;
  }

  public void setArtifactId(String artifactId) {
    getModel().setArtifactId(artifactId);
  }

  public String getArtifactId() {
    return getModel().getArtifactId();
  }

  public void setName(String name) {
    getModel().setName(name);
  }

  public String getName() {
    // TODO: this should not be allowed to be null.
    if (getModel().getName() != null) {
      return getModel().getName();
    } else {
      return getArtifactId();
    }
  }

  public void setVersion(String version) {
    getModel().setVersion(version);
  }

  public String getVersion() {
    String version = getModel().getVersion();

    if ((version == null) && (getModel().getParent() != null)) {
      version = getModel().getParent().getVersion();
    }

    return version;
  }

  public String getPackaging() {
    return getModel().getPackaging();
  }

  public void setPackaging(String packaging) {
    getModel().setPackaging(packaging);
  }

  public void setInceptionYear(String inceptionYear) {
    getModel().setInceptionYear(inceptionYear);
  }

  public String getInceptionYear() {
    return getModel().getInceptionYear();
  }

  public void setUrl(String url) {
    getModel().setUrl(url);
  }

  public String getUrl() {
    return getModel().getUrl();
  }

  public Prerequisites getPrerequisites() {
    return getModel().getPrerequisites();
  }

  public void setIssueManagement(IssueManagement issueManagement) {
    getModel().setIssueManagement(issueManagement);
  }

  public CiManagement getCiManagement() {
    return getModel().getCiManagement();
  }

  public void setCiManagement(CiManagement ciManagement) {
    getModel().setCiManagement(ciManagement);
  }

  public IssueManagement getIssueManagement() {
    return getModel().getIssueManagement();
  }

  public void setDistributionManagement(DistributionManagement distributionManagement) {
    getModel().setDistributionManagement(distributionManagement);
  }

  public DistributionManagement getDistributionManagement() {
    return getModel().getDistributionManagement();
  }

  public void setDescription(String description) {
    getModel().setDescription(description);
  }

  public String getDescription() {
    return getModel().getDescription();
  }

  public void setOrganization(Organization organization) {
    getModel().setOrganization(organization);
  }

  public Organization getOrganization() {
    return getModel().getOrganization();
  }

  public void setScm(Scm scm) {
    getModel().setScm(scm);
  }

  public Scm getScm() {
    return getModel().getScm();
  }

  public void setMailingLists(List<MailingList> mailingLists) {
    getModel().setMailingLists(mailingLists);
  }

  public List<MailingList> getMailingLists() {
    return getModel().getMailingLists();
  }

  public void addMailingList(MailingList mailingList) {
    getModel().addMailingList(mailingList);
  }

  public void setDevelopers(List<Developer> developers) {
    getModel().setDevelopers(developers);
  }

  public List<Developer> getDevelopers() {
    return getModel().getDevelopers();
  }

  public void addDeveloper(Developer developer) {
    getModel().addDeveloper(developer);
  }

  public void setContributors(List<Contributor> contributors) {
    getModel().setContributors(contributors);
  }

  public List<Contributor> getContributors() {
    return getModel().getContributors();
  }

  public void addContributor(Contributor contributor) {
    getModel().addContributor(contributor);
  }

  public void setBuild(Build build) {
    getModel().setBuild(build);
  }

  public Build getBuild() {
    return getModelBuild();
  }

  public List<Resource> getResources() {
    return getBuild().getResources();
  }

  public List<Resource> getTestResources() {
    return getBuild().getTestResources();
  }

  public void addResource(Resource resource) {
    getBuild().addResource(resource);
  }

  public void addTestResource(Resource testResource) {
    getBuild().addTestResource(testResource);
  }

  @Deprecated
  public void setReporting(Reporting reporting) {
    getModel().setReporting(reporting);
  }

  @Deprecated
  public Reporting getReporting() {
    return getModel().getReporting();
  }

  public void setLicenses(List<License> licenses) {
    getModel().setLicenses(licenses);
  }

  public List<License> getLicenses() {
    return getModel().getLicenses();
  }

  public void addLicense(License license) {
    getModel().addLicense(license);
  }

  public void setArtifacts(Set<Artifact> artifacts) {
    this.artifacts = artifacts;

    // flush the calculated artifactMap
    artifactMap = null;
  }

  /**
   * All dependencies that this project has, including transitive ones. Contents are lazily
   * populated, so depending on what phases have run dependencies in some scopes won't be included.
   * eg. if only compile phase has run, dependencies with scope test won't be included.
   * 
   * @return {@link Set} &lt; {@link Artifact} >
   * @see #getDependencyArtifacts() to get only direct dependencies
   */
  public Set<Artifact> getArtifacts() {
    if (artifacts == null) {
      if (artifactFilter == null || resolvedArtifacts == null) {
        artifacts = new LinkedHashSet<Artifact>();
      } else {
        artifacts = new LinkedHashSet<Artifact>(resolvedArtifacts.size() * 2);
        for (Artifact artifact : resolvedArtifacts) {
          if (artifactFilter.include(artifact)) {
            artifacts.add(artifact);
          }
        }
      }
    }
    return artifacts;
  }

  public Map<String, Artifact> getArtifactMap() {
    if (artifactMap == null) {
      artifactMap = ArtifactUtils.artifactMapByVersionlessId(getArtifacts());
    }
    return artifactMap;
  }

  public void setPluginArtifacts(Set<Artifact> pluginArtifacts) {
    this.pluginArtifacts = pluginArtifacts;

    this.pluginArtifactMap = null;
  }

  public Set<Artifact> getPluginArtifacts() {
    if (pluginArtifacts != null) {
      return pluginArtifacts;
    }

    pluginArtifacts = new HashSet<Artifact>();

    if (repositorySystem != null) {
      for (Plugin p : getBuildPlugins()) {
        Artifact artifact = repositorySystem.createPluginArtifact(p);

        if (artifact != null) {
          pluginArtifacts.add(artifact);
        }
      }
    }

    pluginArtifactMap = null;

    return pluginArtifacts;
  }

  public Map<String, Artifact> getPluginArtifactMap() {
    if (pluginArtifactMap == null) {
      pluginArtifactMap = ArtifactUtils.artifactMapByVersionlessId(getPluginArtifacts());
    }

    return pluginArtifactMap;
  }

  @Deprecated
  public void setReportArtifacts(Set<Artifact> reportArtifacts) {
    this.reportArtifacts = reportArtifacts;

    reportArtifactMap = null;
  }

  @Deprecated
  public Set<Artifact> getReportArtifacts() {
    if (reportArtifacts != null) {
      return reportArtifacts;
    }

    reportArtifacts = new HashSet<Artifact>();

    if (repositorySystem != null) {
      for (ReportPlugin p : getReportPlugins()) {
        Plugin pp = new Plugin();
        pp.setGroupId(p.getGroupId());
        pp.setArtifactId(p.getArtifactId());
        pp.setVersion(p.getVersion());

        Artifact artifact = repositorySystem.createPluginArtifact(pp);

        if (artifact != null) {
          reportArtifacts.add(artifact);
        }
      }
    }

    reportArtifactMap = null;

    return reportArtifacts;
  }

  @Deprecated
  public Map<String, Artifact> getReportArtifactMap() {
    if (reportArtifactMap == null) {
      reportArtifactMap = ArtifactUtils.artifactMapByVersionlessId(getReportArtifacts());
    }

    return reportArtifactMap;
  }

  public void setExtensionArtifacts(Set<Artifact> extensionArtifacts) {
    this.extensionArtifacts = extensionArtifacts;

    extensionArtifactMap = null;
  }

  public Set<Artifact> getExtensionArtifacts() {
    if (extensionArtifacts != null) {
      return extensionArtifacts;
    }
    extensionArtifacts = new HashSet<Artifact>();
    List<Extension> extensions = getBuildExtensions();
    if (extensions != null) {
      for (Extension ext : extensions) {
        String version;
        if (StringUtils.isEmpty(ext.getVersion())) {
          version = "RELEASE";
        } else {
          version = ext.getVersion();
        }

        Artifact artifact = repositorySystem.createArtifact(ext.getGroupId(), ext.getArtifactId(), version, null,
            "jar");

        if (artifact != null) {
          extensionArtifacts.add(artifact);
        }
      }
    }
    extensionArtifactMap = null;
    return extensionArtifacts;
  }

  public Map<String, Artifact> getExtensionArtifactMap() {
    if (extensionArtifactMap == null) {
      extensionArtifactMap = ArtifactUtils.artifactMapByVersionlessId(getExtensionArtifacts());
    }

    return extensionArtifactMap;
  }

  public void setParentArtifact(Artifact parentArtifact) {
    this.parentArtifact = parentArtifact;
  }

  public Artifact getParentArtifact() {
    if (parentArtifact == null && model.getParent() != null) {
      Parent p = model.getParent();
      parentArtifact = repositorySystem.createProjectArtifact(p.getGroupId(), p.getArtifactId(), p.getVersion());
    }
    return parentArtifact;
  }

  public List<Repository> getRepositories() {
    return getModel().getRepositories();
  }

  // ----------------------------------------------------------------------
  // Plugins
  // ----------------------------------------------------------------------

  @Deprecated
  public List<ReportPlugin> getReportPlugins() {
    if (getModel().getReporting() == null) {
      return Collections.emptyList();
    }
    return getModel().getReporting().getPlugins();

  }

  public List<Plugin> getBuildPlugins() {
    if (getModel().getBuild() == null) {
      return Collections.emptyList();
    }
    return getModel().getBuild().getPlugins();
  }

  public List<String> getModules() {
    return getModel().getModules();
  }

  public PluginManagement getPluginManagement() {
    PluginManagement pluginMgmt = null;

    Build build = getModel().getBuild();
    if (build != null) {
      pluginMgmt = build.getPluginManagement();
    }

    return pluginMgmt;
  }

  private Build getModelBuild() {
    Build build = getModel().getBuild();

    if (build == null) {
      build = new Build();

      getModel().setBuild(build);
    }

    return build;
  }

  public void setRemoteArtifactRepositories(List<ArtifactRepository> remoteArtifactRepositories) {
    this.remoteArtifactRepositories = remoteArtifactRepositories;
    this.remoteProjectRepositories = RepositoryUtils.toRepos(getRemoteArtifactRepositories());
  }

  public List<ArtifactRepository> getRemoteArtifactRepositories() {
    if (remoteArtifactRepositories == null) {
      remoteArtifactRepositories = new ArrayList<ArtifactRepository>();
    }

    return remoteArtifactRepositories;
  }

  public void setPluginArtifactRepositories(List<ArtifactRepository> pluginArtifactRepositories) {
    this.pluginArtifactRepositories = pluginArtifactRepositories;
    this.remotePluginRepositories = RepositoryUtils.toRepos(getPluginArtifactRepositories());
  }

  /**
   * @return a list of ArtifactRepository objects constructed from the Repository objects returned
   *         by getPluginRepositories.
   */
  public List<ArtifactRepository> getPluginArtifactRepositories() {
    if (pluginArtifactRepositories == null) {
      pluginArtifactRepositories = new ArrayList<ArtifactRepository>();
    }

    return pluginArtifactRepositories;
  }

  public ArtifactRepository getDistributionManagementArtifactRepository() {
    return getArtifact().isSnapshot() && (getSnapshotArtifactRepository() != null) ? getSnapshotArtifactRepository()
        : getReleaseArtifactRepository();
  }

  public List<Repository> getPluginRepositories() {
    return getModel().getPluginRepositories();
  }

  public List<RemoteRepository> getRemoteProjectRepositories() {
    return remoteProjectRepositories;
  }

  public List<RemoteRepository> getRemotePluginRepositories() {
    return remotePluginRepositories;
  }

  public void setActiveProfiles(List<Profile> activeProfiles) {
    this.activeProfiles = activeProfiles;
  }

  public List<Profile> getActiveProfiles() {
    return activeProfiles;
  }

  public void setInjectedProfileIds(String source, List<String> injectedProfileIds) {
    if (injectedProfileIds != null) {
      this.injectedProfileIds.put(source, new ArrayList<String>(injectedProfileIds));
    } else {
      this.injectedProfileIds.remove(source);
    }
  }

  /**
   * Gets the identifiers of all profiles that contributed to this project's effective model. This
   * includes active profiles from the project's POM and all its parent POMs as well as from
   * external sources like the {@code
   * settings.xml}. The profile identifiers are grouped by the identifier of their source, e.g.
   * {@code
   * <groupId>:<artifactId>:<version>} for a POM profile or {@code external} for profiles from the
   * {@code
   * settings.xml}.
   * 
   * @return The identifiers of all injected profiles, indexed by the source from which the profiles
   *         originated, never {@code null}.
   */
  public Map<String, List<String>> getInjectedProfileIds() {
    return this.injectedProfileIds;
  }

  private String logStringForArtifactFile(Artifact a) {
    if (a.getFile() != null) {
      return a.getFile().getAbsolutePath();
    } else {
      return "(no path)";
    }
  }

  /**
   * Add or replace an artifact. In spite of the 'throws' declaration on this API, this method has
   * never thrown an exception since Maven 3.0.x. Historically, it logged and ignored a second
   * addition of the same g/a/v/c/t. Now it replaces the file for the artifact, so that plugins
   * (e.g. shade) can change the pathname of the file for a particular set of coordinates.
   * 
   * @param artifact the artifact to add or replace.
   * @throws DuplicateArtifactAttachmentException
   */
  public void addAttachedArtifact(Artifact artifact) throws DuplicateArtifactAttachmentException {
    List<Artifact> attachedArtifacts = getAttachedArtifacts();
    for (int ax = 0; ax < attachedArtifacts.size(); ax++) {
      Artifact a = attachedArtifacts.get(ax);
      if (a.equals(artifact)) {
        if (logger != null) {
          logger.debug(String.format("Replacing attached artifact %s. Old path %s, new path %s. ", a,
              logStringForArtifactFile(a), logStringForArtifactFile(artifact)));
        }
        attachedArtifacts.set(ax, artifact);
        return;
      }
    }

    getAttachedArtifacts().add(artifact);
  }

  public List<Artifact> getAttachedArtifacts() {
    if (attachedArtifacts == null) {
      attachedArtifacts = new ArrayList<Artifact>();
    }
    return attachedArtifacts;
  }

  public Xpp3Dom getGoalConfiguration(String pluginGroupId, String pluginArtifactId, String executionId,
      String goalId) {
    Xpp3Dom dom = null;

    if (getBuildPlugins() != null) {
      for (Plugin plugin : getBuildPlugins()) {
        if (pluginGroupId.equals(plugin.getGroupId()) && pluginArtifactId.equals(plugin.getArtifactId())) {
          dom = (Xpp3Dom) plugin.getConfiguration();

          if (executionId != null) {
            PluginExecution execution = plugin.getExecutionsAsMap().get(executionId);
            if (execution != null) {
              // NOTE: The PluginConfigurationExpander already merged the plugin-level config in
              dom = (Xpp3Dom) execution.getConfiguration();
            }
          }
          break;
        }
      }
    }

    if (dom != null) {
      // make a copy so the original in the POM doesn't get messed with
      dom = new Xpp3Dom(dom);
    }

    return dom;
  }

  @Deprecated
  public Xpp3Dom getReportConfiguration(String pluginGroupId, String pluginArtifactId, String reportSetId) {
    Xpp3Dom dom = null;

    // ----------------------------------------------------------------------
    // I would like to be able to lookup the Mojo object using a key but
    // we have a limitation in modello that will be remedied shortly. So
    // for now I have to iterate through and see what we have.
    // ----------------------------------------------------------------------

    if (getReportPlugins() != null) {
      for (ReportPlugin plugin : getReportPlugins()) {
        if (pluginGroupId.equals(plugin.getGroupId()) && pluginArtifactId.equals(plugin.getArtifactId())) {
          dom = (Xpp3Dom) plugin.getConfiguration();

          if (reportSetId != null) {
            ReportSet reportSet = plugin.getReportSetsAsMap().get(reportSetId);
            if (reportSet != null) {
              Xpp3Dom executionConfiguration = (Xpp3Dom) reportSet.getConfiguration();
              if (executionConfiguration != null) {
                Xpp3Dom newDom = new Xpp3Dom(executionConfiguration);
                dom = Xpp3Dom.mergeXpp3Dom(newDom, dom);
              }
            }
          }
          break;
        }
      }
    }

    if (dom != null) {
      // make a copy so the original in the POM doesn't get messed with
      dom = new Xpp3Dom(dom);
    }

    return dom;
  }

  public MavenProject getExecutionProject() {
    return (executionProject == null ? this : executionProject);
  }

  public void setExecutionProject(MavenProject executionProject) {
    this.executionProject = executionProject;
  }

  public List<MavenProject> getCollectedProjects() {
    return collectedProjects;
  }

  public void setCollectedProjects(List<MavenProject> collectedProjects) {
    this.collectedProjects = collectedProjects;
  }

  /**
   * Direct dependencies that this project has.
   * 
   * @return {@link Set} &lt; {@link Artifact} >
   * @see #getArtifacts() to get all transitive dependencies
   */
  public Set<Artifact> getDependencyArtifacts() {
    return dependencyArtifacts;
  }

  public void setDependencyArtifacts(Set<Artifact> dependencyArtifacts) {
    this.dependencyArtifacts = dependencyArtifacts;
  }

  public void setReleaseArtifactRepository(ArtifactRepository releaseArtifactRepository) {
    this.releaseArtifactRepository = releaseArtifactRepository;
  }

  public void setSnapshotArtifactRepository(ArtifactRepository snapshotArtifactRepository) {
    this.snapshotArtifactRepository = snapshotArtifactRepository;
  }

  public void setOriginalModel(Model originalModel) {
    this.originalModel = originalModel;
  }

  public Model getOriginalModel() {
    return originalModel;
  }

  public void setManagedVersionMap(Map<String, Artifact> map) {
    managedVersionMap = map;
  }

  public Map<String, Artifact> getManagedVersionMap() {
    if (managedVersionMap != null) {
      return managedVersionMap;
    }

    Map<String, Artifact> map = null;
    if (repositorySystem != null) {

      List<Dependency> deps;
      DependencyManagement dependencyManagement = getDependencyManagement();
      if ((dependencyManagement != null) && ((deps = dependencyManagement.getDependencies()) != null)
          && (deps.size() > 0)) {
        map = new HashMap<String, Artifact>();
        for (Dependency d : dependencyManagement.getDependencies()) {
          Artifact artifact = repositorySystem.createDependencyArtifact(d);

          if (artifact == null) {
            map = Collections.emptyMap();
          }

          map.put(d.getManagementKey(), artifact);
        }
      } else {
        map = Collections.emptyMap();
      }
    }
    managedVersionMap = map;
    return managedVersionMap;
  }

  @Override
  public boolean equals(Object other) {
    if (other == this) {
      return true;
    } else if (!(other instanceof MavenProject)) {
      return false;
    }

    MavenProject that = (MavenProject) other;

    return eq(getArtifactId(), that.getArtifactId()) && eq(getGroupId(), that.getGroupId())
        && eq(getVersion(), that.getVersion());
  }

  private static <T> boolean eq(T s1, T s2) {
    return (s1 != null) ? s1.equals(s2) : s2 == null;
  }

  @Override
  public int hashCode() {
    int hash = 17;
    hash = 31 * hash + getGroupId().hashCode();
    hash = 31 * hash + getArtifactId().hashCode();
    hash = 31 * hash + getVersion().hashCode();
    return hash;
  }

  public List<Extension> getBuildExtensions() {
    Build build = getBuild();
    if ((build == null) || (build.getExtensions() == null)) {
      return Collections.emptyList();
    } else {
      return build.getExtensions();
    }
  }

  public void addProjectReference(MavenProject project) {
    projectReferences.put(getProjectReferenceId(project.getGroupId(), project.getArtifactId(), project.getVersion()),
        project);
  }

  /**
   * @deprecated Use MavenProjectHelper.attachArtifact(..) instead.
   */
  @Deprecated
  public void attachArtifact(String type, String classifier, File file) {
  }

  public Properties getProperties() {
    return getModel().getProperties();
  }

  public List<String> getFilters() {
    return getBuild().getFilters();
  }

  public Map<String, MavenProject> getProjectReferences() {
    return projectReferences;
  }

  public boolean isExecutionRoot() {
    return executionRoot;
  }

  public void setExecutionRoot(boolean executionRoot) {
    this.executionRoot = executionRoot;
  }

  public String getDefaultGoal() {
    return getBuild() != null ? getBuild().getDefaultGoal() : null;
  }

  public Plugin getPlugin(String pluginKey) {
    return getBuild().getPluginsAsMap().get(pluginKey);
  }

  /**
   * Default toString
   */
  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder(128);
    sb.append("MavenProject: ");
    sb.append(getGroupId());
    sb.append(":");
    sb.append(getArtifactId());
    sb.append(":");
    sb.append(getVersion());
    sb.append(" @ ");

    try {
      sb.append(getFile().getPath());
    } catch (NullPointerException e) {
      // don't log it.
    }

    return sb.toString();
  }

  /**
   * @deprecated Use {@link org.apache.maven.model.io.ModelWriter}.
   */
  @Deprecated
  public void writeModel(Writer writer) throws IOException {
    MavenXpp3Writer pomWriter = new MavenXpp3Writer();
    pomWriter.write(writer, getModel());
  }

  /**
   * @deprecated Use {@link org.apache.maven.model.io.ModelWriter}.
   */
  @Deprecated
  public void writeOriginalModel(Writer writer) throws IOException {
    MavenXpp3Writer pomWriter = new MavenXpp3Writer();
    pomWriter.write(writer, getOriginalModel());
  }

  /**
   * @throws CloneNotSupportedException
   * @since 2.0.9
   */
  @Override
  public MavenProject clone() {
    MavenProject clone;
    try {
      clone = (MavenProject) super.clone();
    } catch (CloneNotSupportedException e) {
      throw new UnsupportedOperationException(e);
    }

    clone.deepCopy(this);

    return clone;
  }

  protected void setModel(Model model) {
    this.model = model;
  }

  protected void setAttachedArtifacts(List<Artifact> attachedArtifacts) {
    this.attachedArtifacts = attachedArtifacts;
  }

  protected void setCompileSourceRoots(List<String> compileSourceRoots) {
    this.compileSourceRoots = compileSourceRoots;
  }

  protected void setTestCompileSourceRoots(List<String> testCompileSourceRoots) {
    this.testCompileSourceRoots = testCompileSourceRoots;
  }

  protected void setScriptSourceRoots(List<String> scriptSourceRoots) {
    this.scriptSourceRoots = scriptSourceRoots;
  }

  protected ArtifactRepository getReleaseArtifactRepository() {
    if (releaseArtifactRepository == null) {
      if (getDistributionManagement() != null && getDistributionManagement().getRepository() != null) {
        checkProjectBuildingRequest();
        try {
          ArtifactRepository repo = repositorySystem
              .buildArtifactRepository(getDistributionManagement().getRepository());
          repositorySystem.injectProxy(projectBuilderConfiguration.getRepositorySession(), Arrays.asList(repo));
          repositorySystem.injectAuthentication(projectBuilderConfiguration.getRepositorySession(),
              Arrays.asList(repo));
          setReleaseArtifactRepository(repo);
        } catch (InvalidRepositoryException e) {
          throw new IllegalStateException("Failed to create release distribution repository for " + getId(), e);
        }
      }
    }

    return releaseArtifactRepository;
  }

  protected ArtifactRepository getSnapshotArtifactRepository() {
    if (snapshotArtifactRepository == null) {
      if (getDistributionManagement() != null && getDistributionManagement().getSnapshotRepository() != null) {
        checkProjectBuildingRequest();
        try {
          ArtifactRepository repo = repositorySystem
              .buildArtifactRepository(getDistributionManagement().getSnapshotRepository());
          repositorySystem.injectProxy(projectBuilderConfiguration.getRepositorySession(), Arrays.asList(repo));
          repositorySystem.injectAuthentication(projectBuilderConfiguration.getRepositorySession(),
              Arrays.asList(repo));
          setSnapshotArtifactRepository(repo);
        } catch (InvalidRepositoryException e) {
          throw new IllegalStateException("Failed to create snapshot distribution repository for " + getId(), e);
        }
      }
    }

    return snapshotArtifactRepository;
  }

  @Deprecated
  public Artifact replaceWithActiveArtifact(Artifact pluginArtifact) {
    return pluginArtifact;
  }

  private void deepCopy(MavenProject project) {
    // disown the parent

    // copy fields
    setFile(project.getFile());

    // don't need a deep copy, they don't get modified or added/removed to/from - but make them
    // unmodifiable to be
    // sure!
    if (project.getDependencyArtifacts() != null) {
      setDependencyArtifacts(Collections.unmodifiableSet(project.getDependencyArtifacts()));
    }

    if (project.getArtifacts() != null) {
      setArtifacts(Collections.unmodifiableSet(project.getArtifacts()));
    }

    if (project.getParentFile() != null) {
      parentFile = new File(project.getParentFile().getAbsolutePath());
    }

    if (project.getPluginArtifacts() != null) {
      setPluginArtifacts(Collections.unmodifiableSet(project.getPluginArtifacts()));
    }

    if (project.getReportArtifacts() != null) {
      setReportArtifacts(Collections.unmodifiableSet(project.getReportArtifacts()));
    }

    if (project.getExtensionArtifacts() != null) {
      setExtensionArtifacts(Collections.unmodifiableSet(project.getExtensionArtifacts()));
    }

    setParentArtifact((project.getParentArtifact()));

    if (project.getRemoteArtifactRepositories() != null) {
      setRemoteArtifactRepositories(Collections.unmodifiableList(project.getRemoteArtifactRepositories()));
    }

    if (project.getPluginArtifactRepositories() != null) {
      setPluginArtifactRepositories((Collections.unmodifiableList(project.getPluginArtifactRepositories())));
    }

    if (project.getActiveProfiles() != null) {
      setActiveProfiles((Collections.unmodifiableList(project.getActiveProfiles())));
    }

    if (project.getAttachedArtifacts() != null) {
      // clone properties modifyable by plugins in a forked lifecycle
      setAttachedArtifacts(new ArrayList<Artifact>(project.getAttachedArtifacts()));
    }

    if (project.getCompileSourceRoots() != null) {
      // clone source roots
      setCompileSourceRoots((new ArrayList<String>(project.getCompileSourceRoots())));
    }

    if (project.getTestCompileSourceRoots() != null) {
      setTestCompileSourceRoots((new ArrayList<String>(project.getTestCompileSourceRoots())));
    }

    if (project.getScriptSourceRoots() != null) {
      setScriptSourceRoots((new ArrayList<String>(project.getScriptSourceRoots())));
    }

    if (project.getModel() != null) {
      setModel(project.getModel().clone());
    }

    if (project.getOriginalModel() != null) {
      setOriginalModel(project.getOriginalModel());
    }

    setExecutionRoot(project.isExecutionRoot());

    if (project.getArtifact() != null) {
      setArtifact(ArtifactUtils.copyArtifact(project.getArtifact()));
    }

    if (project.getManagedVersionMap() != null) {
      setManagedVersionMap(new HashMap<String, Artifact>(project.getManagedVersionMap()));
    }

    lifecyclePhases.addAll(project.lifecyclePhases);
  }

  private void addArtifactPath(Artifact artifact, List<String> classpath) {
    File file = artifact.getFile();
    if (file != null) {
      classpath.add(file.getPath());
    }
  }

  private static String getProjectReferenceId(String groupId, String artifactId, String version) {
    StringBuilder buffer = new StringBuilder(128);
    buffer.append(groupId).append(':').append(artifactId).append(':').append(version);
    return buffer.toString();
  }

  /**
   * Sets the value of the context value of this project identified by the given key. If the
   * supplied value is <code>null</code>, the context value is removed from this project.
   * 
   * Context values are intended to allow core extensions to associate derived state with project
   * instances.
   */
  public void setContextValue(String key, Object value) {
    if (context == null) {
      context = new HashMap<String, Object>();
    }
    if (value != null) {
      context.put(key, value);
    } else {
      context.remove(key);
    }
  }

  /**
   * Returns context value of this project associated with the given key or null if this project has
   * no such value.
   */
  public Object getContextValue(String key) {
    if (context == null) {
      return null;
    }
    return context.get(key);
  }

  /**
   * Sets the project's class realm. <strong>Warning:</strong> This is an internal utility method
   * that is only public for technical reasons, it is not part of the public API. In particular,
   * this method can be changed or deleted without prior notice and must not be used by plugins.
   * 
   * @param classRealm The class realm hosting the build extensions of this project, may be
   *        {@code null}.
   */
  public void setClassRealm(ClassRealm classRealm) {
    this.classRealm = classRealm;
  }

  /**
   * Gets the project's class realm. This class realm hosts the build extensions of the project.
   * <strong>Warning:</strong> This is an internal utility method that is only public for technical
   * reasons, it is not part of the public API. In particular, this method can be changed or deleted
   * without prior notice and must not be used by plugins.
   * 
   * @return The project's class realm or {@code null}.
   */
  public ClassRealm getClassRealm() {
    return classRealm;
  }

  /**
   * Sets the artifact filter used to exclude shared extension artifacts from plugin realms.
   * <strong>Warning:</strong> This is an internal utility method that is only public for technical
   * reasons, it is not part of the public API. In particular, this method can be changed or deleted
   * without prior notice and must not be used by plugins.
   * 
   * @param extensionDependencyFilter The dependency filter to apply to plugins, may be
   *        {@code null}.
   */
  public void setExtensionDependencyFilter(DependencyFilter extensionDependencyFilter) {
    this.extensionDependencyFilter = extensionDependencyFilter;
  }

  /**
   * Gets the dependency filter used to exclude shared extension artifacts from plugin realms.
   * <strong>Warning:</strong> This is an internal utility method that is only public for technical
   * reasons, it is not part of the public API. In particular, this method can be changed or deleted
   * without prior notice and must not be used by plugins.
   * 
   * @return The dependency filter or {@code null}.
   */
  public DependencyFilter getExtensionDependencyFilter() {
    return extensionDependencyFilter;
  }

  /**
   * Sets the transitive dependency artifacts that have been resolved/collected for this project.
   * <strong>Warning:</strong> This is an internal utility method that is only public for technical
   * reasons, it is not part of the public API. In particular, this method can be changed or deleted
   * without prior notice and must not be used by plugins.
   * 
   * @param artifacts The set of artifacts, may be {@code null}.
   */
  public void setResolvedArtifacts(Set<Artifact> artifacts) {
    this.resolvedArtifacts = (artifacts != null) ? artifacts : Collections.<Artifact> emptySet();
    this.artifacts = null;
    this.artifactMap = null;
  }

  /**
   * Sets the scope filter to select the artifacts being exposed to the currently executed mojo.
   * <strong>Warning:</strong> This is an internal utility method that is only public for technical
   * reasons, it is not part of the public API. In particular, this method can be changed or deleted
   * without prior notice and must not be used by plugins.
   * 
   * @param artifactFilter The artifact filter, may be {@code null} to exclude all artifacts.
   */
  public void setArtifactFilter(ArtifactFilter artifactFilter) {
    this.artifactFilter = artifactFilter;
    this.artifacts = null;
    this.artifactMap = null;
  }

  /**
   * <strong>Warning:</strong> This is an internal utility method that is only public for technical
   * reasons, it is not part of the public API. In particular, this method can be changed or deleted
   * without prior notice and must not be used by plugins.
   * 
   * @param phase The phase to check for, must not be {@code null}.
   * @return {@code true} if the phase has been seen.
   */
  public boolean hasLifecyclePhase(String phase) {
    return lifecyclePhases.contains(phase);
  }

  /**
   * <strong>Warning:</strong> This is an internal utility method that is only public for technical
   * reasons, it is not part of the public API. In particular, this method can be changed or deleted
   * without prior notice and must not be used by plugins.
   * 
   * @param lifecyclePhase The lifecycle phase to add, must not be {@code null}.
   */
  public void addLifecyclePhase(String lifecyclePhase) {
    lifecyclePhases.add(lifecyclePhase);
  }

  /**
   * Gets the project building request from which this project instance was created.
   * <strong>Warning:</strong> This is an utility method that is meant to assist integrators of
   * Maven, it must not be used by Maven plugins.
   * 
   * @return The project building request or {@code null}.
   * @since 2.1
   */
  public ProjectBuildingRequest getProjectBuildingRequest() {
    return projectBuilderConfiguration;
  }

  /**
   * Sets the project building request from which this project instance was created.
   * <strong>Warning:</strong> This is an utility method that is meant to assist integrators of
   * Maven, it must not be used by Maven plugins.
   * 
   * @param projectBuildingRequest The project building request, may be {@code null}.
   * @since 2.1
   */
  public void setProjectBuildingRequest(ProjectBuildingRequest projectBuildingRequest) {
    projectBuilderConfiguration = projectBuildingRequest;
  }

  private void checkProjectBuildingRequest() {
    if (projectBuilderConfiguration == null) {
      throw new IllegalStateException("project building request missing");
    }
  }

}
